import { pushTarget, popTarget } from './dep'
import { queueWatcher } from './scheduler'
import { isObject } from '../util/index'

let id = 0

export default class Watcher {
  constructor(vm, exprOrFn, cb, options) {
    this.vm = vm
    this.exprOrFn = exprOrFn
    this.cb = cb
    this.options = options
    this.id = id++

    this.deps = []
    this.depsId = new Set()

    this.user = options.user
    this.lazy = options.lazy
    this.dirty = this.lazy

    if (typeof exprOrFn === 'function') {
      this.getter = exprOrFn
    } else {
      this.getter = function () {
        let path = exprOrFn.split('.')
        let obj = vm
        for (let k of path) {
          obj = obj[k]
        }
        return obj
      }
    }

    // this.value = this.get()
    this.value = this.lazy ? void 0 : this.get()

    // this.get()
    // this.update()
  }
  get() {
    pushTarget(this)
    const res = this.getter.call(this.vm)
    // this.getter()
    popTarget()
    return res
  }
  addDep(dep) {
    let id = dep.id
    if (!this.depsId.has(id)) {
      this.depsId.add(id)
      this.deps.push(dep)
      dep.addSub(this)
    }
  }
  update() {
    // this.get()
    if (this.lazy) {
      this.dirty = true
    } else {
      queueWatcher(this)
    }
  }
  run() {
    const newVal = this.get()
    const oldVal = this.value
    this.value = newVal
    if (this.user) {
      if (newVal !== oldVal || isObject(newVal)) {
        this.cb.call(this.vm, newVal, oldVal)
      }
    } else {
      this.cb.call(this.vm)
    }
    // this.get()
  }
  evaluate() {
    this.value = this.get()
    this.dirty = false
  }
  depend() {
    let i = this.deps.length
    while (i--) {
      this.deps[i].depend()
    }
  }
}
